package com.fuluwa.application.pay.util;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import com.fuluwa.application.ErrConstant;
import com.fuluwa.application.FlwBizException;
import com.fuluwa.infra.utils.StringUtil;

public class ParametersUtil {

	private static final List<String> excludeList = new ArrayList<String>();

	static {
		excludeList.add("sign");
		excludeList.add("class");
		// excludeList.add("partner");
	}

	/**
	 * POJO转REST参数，暂不支持除LIST以外集合，不支持Date等类型的cover（有需要再加）
	 * TODO：增加Sign注解，标示需要做签名的参数，转换同时生产签名
	 * 
	 * @param inputObj
	 * @return
	 */
	public static String coverInputObjToString(Object inputObj) throws Exception {
		StringBuilder sb = new StringBuilder();
		if (null == inputObj) {
			return sb.toString();
		}
		String clazzName = inputObj.getClass().getSimpleName();
		Field[] fields = inputObj.getClass().getDeclaredFields();
		for (Field field : fields) {
			String fieldName = field.getName();
			if (excludeList.contains(fieldName)) {
				continue;
			}
			// 获取field的值
			Object parmeterValue = getFieldValue(inputObj, fieldName);
			FiledNotNull filedNotNull = field.getAnnotation(FiledNotNull.class);
			if (null == parmeterValue) {
				if (null != filedNotNull) {
					throw FlwBizException.createInstance(ErrConstant.ERRCODE_UNIPAY_PARAMETER_CAN_NOT_BE_NULL,
							"所需入参字段不能为空clazzName=" + clazzName + " fieldName:" + fieldName);
				} else {
					continue;
				}
			}
			int maxLenght = Integer.MAX_VALUE;
			FiledMaxLenght filedMaxLenght = field.getAnnotation(FiledMaxLenght.class);
			if (null != filedMaxLenght) {
				maxLenght = filedMaxLenght.lenght();
			}

			FieldMappingOption mappingOption = field.getAnnotation(FieldMappingOption.class);
			// 没有映射注解的时候，这个属性不是List,而且是简单类型,直接toString
			if (null == mappingOption) {
				String parmeterName = StringUtil.changeJavaStyleToDbcloumStyle(fieldName);
				String realValue = parmeterValue.toString();
				appendString(sb, parmeterName, realValue, maxLenght);
			} else {
				// 有映射注解的时候
				String optionMappingName = mappingOption.mappingName();
				FiledMappingType filedMappingType = mappingOption.mappingType();
				// 先判断是不是list
				if (List.class.isAssignableFrom(parmeterValue.getClass())) {
					appendList(sb, (List) parmeterValue, optionMappingName, filedMappingType, maxLenght);

				} else {
					if (FiledMappingType.SIMPLE == filedMappingType) {
						// 不是list，并且是简单类型，那么只映射名字就行了
						String realValue = parmeterValue.toString();
						appendString(sb, optionMappingName, realValue, maxLenght);
					} else {
						// 不是list，但是是复杂类型,进行递归，拆分出简单类型为止
						sb.append(coverInputObjToString(parmeterValue));
					}
				}
			}
		}
		return sb.toString();
	}

	/**
	 * 把对象中的属性字段根据要求转换成Map
	 * 
	 * @param inputObj
	 * @return
	 */
	public static Map<String, String> coverInputObjToMap(Object inputObj, boolean needSort) throws Exception {
		Map<String, String> result = null;
		// 判断输出结果是否需要排序
		if (needSort) {
			result = new TreeMap<String, String>();
		} else {
			result = new HashMap<String, String>();
		}

		// 如果输入对象为空，直接返回
		if (null == inputObj) {
			return result;
		}

		String clazzName = inputObj.getClass().getSimpleName();
		Field[] fields = inputObj.getClass().getDeclaredFields();
		for (Field field : fields) {
			String fieldName = field.getName();
			if (excludeList.contains(fieldName)) {
				continue;
			}
			// 获取field的值
			Object parmeterValue = getFieldValue(inputObj, fieldName);
			FiledNotNull filedNotNull = field.getAnnotation(FiledNotNull.class);
			if (null == parmeterValue) {
				if (null != filedNotNull) {
					throw FlwBizException.createInstance(ErrConstant.ERRCODE_UNIPAY_PARAMETER_CAN_NOT_BE_NULL,
							"所需入参字段不能为空clazzName=" + clazzName + " fieldName:" + fieldName);
				} else {
					continue;
				}
			}
			int maxLenght = Integer.MAX_VALUE;
			FiledMaxLenght filedMaxLenght = field.getAnnotation(FiledMaxLenght.class);
			if (null != filedMaxLenght) {
				maxLenght = filedMaxLenght.lenght();
			}

			FieldMappingOption mappingOption = field.getAnnotation(FieldMappingOption.class);
			// 没有映射注解的时候，这个属性不是List,而且是简单类型,直接toString
			if (null == mappingOption) {
				String parmeterName = StringUtil.changeJavaStyleToDbcloumStyle(fieldName);
				String realValue = parmeterValue.toString();
				appendString(result, parmeterName, realValue, maxLenght);
			} else {
				// 有映射注解的时候
				String optionMappingName = mappingOption.mappingName();
				FiledMappingType filedMappingType = mappingOption.mappingType();
				// 先判断是不是list
				if (List.class.isAssignableFrom(parmeterValue.getClass())) {
					appendList(result, (List) parmeterValue, optionMappingName, filedMappingType, maxLenght);

				} else {
					if (FiledMappingType.SIMPLE == filedMappingType) {
						// 不是list，并且是简单类型，那么只映射名字就行了
						String realValue = parmeterValue.toString();
						appendString(result, optionMappingName, realValue, maxLenght);
					} else {
						// 不是list，但是是复杂类型,进行递归，拆分出简单类型为止
						coverInputObjToMap(parmeterValue, needSort);
					}
				}
			}
		}
		return result;
	}

	private static Object getFieldValue(Object inputObj, String fieldName) throws Exception {
		Object parmeterValue = null;
		String getMethodName = "get" + StringUtil.changeThefirstCharToUppercase(fieldName);
		Method getMethod = inputObj.getClass().getMethod(getMethodName, new Class[] {});
		parmeterValue = getMethod.invoke(inputObj, new Object[] {});
		return parmeterValue;
	}

	private static void appendList(StringBuilder sb, List<Object> parmeterValue, String optionMappingName,
			FiledMappingType filedMappingType, int maxLenght) throws UnsupportedEncodingException, Exception {
		// 是list，首先取出来list，去遍历
		// 是简单类型
		if (FiledMappingType.SIMPLE == filedMappingType) {
			for (int i = 0; i < parmeterValue.size(); i++) {
				String parameterName = optionMappingName.replace("{index}", String.valueOf(i));
				String realValue = parmeterValue.get(i).toString();

				appendString(sb, parameterName, realValue, maxLenght);
			}
		} else {
			// 复杂类型
			for (int i = 0; i < parmeterValue.size(); i++) {
				String parameterName = optionMappingName.replace("{index}", String.valueOf(i));
				String realValue = URLEncoder.encode(coverInputObjToString(parmeterValue.get(i)), "UTF-8");
				appendString(sb, parameterName, realValue, maxLenght);
			}
		}
	}

	private static void appendList(Map<String, String> map, List<Object> parmeterValue, String optionMappingName,
			FiledMappingType filedMappingType, int maxLenght) throws UnsupportedEncodingException, Exception {
		// 是list，首先取出来list，去遍历
		// 是简单类型
		if (FiledMappingType.SIMPLE == filedMappingType) {
			for (int i = 0; i < parmeterValue.size(); i++) {
				String parameterName = optionMappingName.replace("{index}", String.valueOf(i));
				String realValue = parmeterValue.get(i).toString();

				appendString(map, parameterName, realValue, maxLenght);
			}
		} else {
			// 复杂类型
			for (int i = 0; i < parmeterValue.size(); i++) {
				String parameterName = optionMappingName.replace("{index}", String.valueOf(i));
				String realValue = URLEncoder.encode(coverInputObjToString(parmeterValue.get(i)), "UTF-8");
				appendString(map, parameterName, realValue, maxLenght);
			}
		}
	}

	private static void appendString(StringBuilder sb, String optionMappingName, String realValue, int maxLenght) {
		try {
			if (realValue.getBytes("Unicode").length > maxLenght) {
				realValue = StringUtil.bSubstring(realValue, maxLenght);
			}
		} catch (Exception e) {
			// TODO
		}
		sb.append(optionMappingName).append("=").append(realValue).append("&");
	}

	private static void appendString(Map<String, String> map, String optionMappingName, String realValue, int maxLenght) {
		try {
			if (realValue.getBytes("Unicode").length > maxLenght) {
				realValue = StringUtil.bSubstring(realValue, maxLenght);
			}
		} catch (Exception e) {
			// TODO
		}
		map.put(optionMappingName, realValue);
	}

	public static void main(String[] args) throws UnsupportedEncodingException {
		System.out
				.println(URLDecoder
						.decode("trade0=partner_trade_no%3D9383785559%26payment_type%3Dmedium%26provider_partner%3D930000110%26item_name%3D%E6%88%91%E6%98%AF%E5%95%86%E5%93%81%E6%88%91%E6%98%AF%E5%95%86%E5%93%81%E6%88%91%E6%98%AF%E5%95%86%E5%93%81%E6%88%91%E6%98%AF%E5%95%86%26item_type%3Dphysical%26automatic_delivery%3Dfalse%26buyer_uin%3D381936231%26seller_uin%3D930000110%26buyer_uid%3D381936231%26seller_uid%3D930000110%26total_amount%3D1%26item_amount%3D1%26freight_amount%3D0%26client_ip%3D127.0.0.1%26&partner=100003",
								"utf-8"));
	}
}
